/* Definition of typechecking and evaluation functions for AST */

#include <sstream>
#include <string>
#include <algorithm>
#include <iostream>

#include "ast.h"
#include "symboltable.h"
#include "bytechunk.h"
#include "module.h"
#include "stringparser.h"
#include "exception.h"

using std::string;
using std::stringstream;
using std::vector;

/*
 * Context methods
 */

void EvalContext::DefineLabel(Label* label)
{
	if(!labels)
		throw Exception("context missing labels table");
	labels->DefineLabel(label);
}

void EvalContext::DefineLabel(const string& name, Label* label)
{
	if(!labels)
		throw Exception("context missing labels table");
	labels->DefineLabel(name, label);
}

string EvalContext::GetUniqueLabelName()
{
	if(!module)
		throw Exception("context missing module");
	return module->GetUniqueLabelName();
}

/*
 * AST node constructors/destructors
 */

CommandDef::~CommandDef()
{
	delete body;
	//delete scope;
}

IfStatement::~IfStatement() {
	delete condition;
	delete thenstmt;
	delete elsestmt;
	//delete endlabel;
	//delete falselabel;
}

MenuStatement::~MenuStatement() {
	while(!exprs.empty()) {
		delete exprs.back();
		exprs.pop_back();
	}
	while(!stmts.empty()) {
		delete stmts.back();
		stmts.pop_back();
	}
}


/*
 * Root table construction
 */

void Block::BuildRootTable(SymbolTable* root, bool atroot, bool labelsok)
{
	for(unsigned int i = 0; i < stmts.size(); ++i)
		stmts[i]->BuildRootTable(root, false, true);
}

void IfStatement::BuildRootTable(SymbolTable* root, bool atroot, bool labelsok)
{
	thenstmt->BuildRootTable(root, false, true);
	if(elsestmt)
		elsestmt->BuildRootTable(root, false, true);
}

void MenuStatement::BuildRootTable(SymbolTable* root, bool atroot, bool labelsok)
{
	for(unsigned int i = 0; i < stmts.size(); ++i)
		stmts[i]->BuildRootTable(root, false, true);
}

void ConstDef::BuildRootTable(SymbolTable* root, bool atroot, bool labelsok)
{
	if(!atroot) {
		Error("constants can only be defined at global scope");
		return;
	}
	if(root->Lookup(this->name) != NULL) {
		string err = "repeat definition of identifier '" + name + "'";
		Error(err);
		return;
	}
	root->Define(this->name, this);
}


void CommandDef::BuildRootTable(SymbolTable* root, bool atroot, bool labelsok)
{
	if(!atroot) {
		Error("commands can only be defined at global scope");
		return;
	}
	if(root->Lookup(this->name) != NULL) {
		string err = "repeat definition of identifier '" + name + "'";
		Error(err);
		return;
	}
	
	root->Define(this->name, this);

	this->parentScope = root;

	// Create a temporary scope, just to check for repeat parameter definitions
	SymbolTable* scope = new SymbolTable(root);
	for(unsigned int i = 0; i < args.size(); ++i) {
		if(scope->Define(args[i], NULL)) {
			string err = "repeat definition of parameter '" + args[i] + "'";
			Error(err);
		}
	}
	delete scope;
}


void Label::BuildRootTable(SymbolTable* scope, bool atroot, bool labelsok)
{
	if(scope->Get(this->name) != NULL ||
		scope->GetLabel(this->name) != NULL) {
		string err = "repeat definition of identifier '" + name + "'";
		Error(err);
		return;
	}

	if(atroot) {
		scope->DefineLabel(this);
	}
	else {
		scope->DefineLabel(new Label(linenumber, name, e));
	}
}

void Program::BuildRootTable(SymbolTable *root)
{
	for(unsigned int i = 0; i < stmts.size(); ++i)
		// Only the statements at the global level are allowed
		// to define consts and commands (for now)
		stmts[i]->BuildRootTable(root, true, true);
}


/*
 * Code lowering
 */

void Block::Evaluate(SymbolTable *env, EvalContext& context, bool asbool)
{
	// NOTE: If we decide to introduce block scoping, we would use a chained
	// context for each child statement.
	for(unsigned int i = 0; i < stmts.size(); ++i)
		stmts[i]->Evaluate(env, context);
}

void IfStatement::Evaluate(SymbolTable *env, EvalContext& context, bool asbool)
{
	/*
	 * Lowering an if statement:
	 *
	 *  [condition]
	 *  [iffalse goto falselbl]
	 *  [thenstmt]
	 *  [goto endlbl]
	 * falselbl:
	 *  [elsestmt]
	 * endlbl:
	 */

	ByteChunk* output = context.output;
	
	// Create internal labels
	string labelbase = context.GetUniqueLabelName();

	Label* endlabel = new Label(labelbase + ".end");
	Label* falselabel = new Label(labelbase + ".false");
	context.DefineLabel(endlabel);
	context.DefineLabel(falselabel);

	// First, we evaluate the condition
	condition->Evaluate(env, context, true);

	// Then, we output an "iffalse goto false" instruction, and register a jump reference
	output->Code("1B 02 FF FF FF FF");
	output->AddReference(output->GetPos() - 4, falselabel);

	// Evaluate the "then" statement
	thenstmt->Evaluate(env, context);

	// Add a "goto end"
	// TODO: strictly speaking, we can dispense with this last goto when
	// there is no 'else' clause. We'll leave it here for now until we
	// get the first round of regression tests in place, and then we'll
	// update it along with the other evaluation refactoring.

	output->Code("0A FF FF FF FF");
	output->AddReference(output->GetPos() - 4, endlabel);

	// Set the position of the "false" label
	falselabel->SetTarget(output->GetPos());

	// Evaluate the "else" statement
	if(elsestmt)
		elsestmt->Evaluate(env, context);

	// Set the position of the "end" label
	endlabel->SetTarget(output->GetPos());
}


void MenuStatement::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	// Lowering a menu statement:
	// [19 02][option][02] - for each option
	// [1C 0C $cols][11][12]
	// [09 $num (statementjmps)]
	// [goto end]
	// [statement][goto end] - for each statement
	// label end:

	ByteChunk* output = context.output;

	// Create internal labels
	vector<Label*> labels;
	string labelbase = context.GetUniqueLabelName();

	for(unsigned int i = 0; i < exprs.size(); ++i) {
		std::stringstream ss;
		ss << ".opt" << i;
		Label* label = new Label(labelbase + ss.str());
		labels.push_back(label);

		context.DefineLabel(label);
	}
	Label* endlabel = new Label(labelbase + ".end");
	context.DefineLabel(endlabel);


	// First, output the options between [19 02] and [02] codes
	for(unsigned int i = 0; i < exprs.size(); ++i) {
		output->Code("19 02");
		exprs[i]->Evaluate(scope, context);
		output->Code("02");
	}

	// Next, output the option display commands
	// If we're only using two options, and no number of columns was specified,
	// use "1C 07", otherwise, use "1C 0C".
	if(exprs.size() == 2 && defcolumns)
		output->Code("1C 07");
	else
		output->Code("1C 0C");

	output->Byte(columns);// write exactly one byte for the column count
	output->Code("11 12");

	// Next, the multi-jump code
	output->Code("09");
	output->Byte(stmts.size());// write exactly one byte for the option count
	for(unsigned int i = 0; i < stmts.size(); ++i) {
		output->Code("FF FF FF FF");
		output->AddReference(output->GetPos() - 4, labels[i]);
	}

	// Add a jump to the "default" option after the multi-jump, or end if no default
	output->Code("0A FF FF FF FF");
	if(defaultopt != -1)
		output->AddReference(output->GetPos() - 4, labels[defaultopt]);
	else
		output->AddReference(output->GetPos() - 4, endlabel);


	// Finally, write out all the options, with a "goto end" after each
	// At each point we set the position of the relevant label.
	for(unsigned int i = 0; i < stmts.size(); ++i)
	{
		labels[i]->SetTarget(output->GetPos());
		stmts[i]->Evaluate(scope, context);

		// Add a "goto end" after every statement, in case it falls through
		output->Code("0A FF FF FF FF");
		output->AddReference(output->GetPos() - 4, endlabel);
	}

	// Last step: set position of the "end" label
	endlabel->SetTarget(output->GetPos());
}


void CommandDef::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	// Command definitions actually don't do anything on the evaluation/lowering pass.
	// Their symbols are defined in the initial BuildRootTable pass, and their subnodes
	// are not evaluated except when an IdentExpr that calls a command is evaluated.
	// At that point, the caller binds the parameters to the argument symbols in the
	// command's local scope and calls CommandDef::Invoke, which evaluates its subnodes
	// in that scope.
}

void CommandDef::Invoke(EvalContext& context, const vector<Expression*>& args)
{
	if(executing) {
		// TODO: this recursion protection also prevents simple composition,
		// e.g., foo(foo("hi")). We should try to find a better way of detecting
		// recursion.

		// Basically, we should notice that while we _are_ evaluating the function
		// within itself, or rather, evaluating one of its argument expressions
		// requires making another call to the function, this will not lead to
		// infinite recursion.

		// It only looks that way because of lazy evaluation. Bottom line, when
		// evaluating a parameter ID, we should turn off the recursion check.

		Error("recursion detected in evaluation of command '" + this->name + "'");
		return;
	}
	/* NOTE: args check responsibility moved to caller
		if(args.size() != this->args.size()) {
		Error("incorrect number of parameters to command '" + this->name + "'");
		return;
	}*/
	executing = true;

	SymbolTable* scope = new SymbolTable( this->parentScope );

	// First, bind the args to the local scope
	for(unsigned int i = 0; i < args.size(); ++i) {
		scope->Define(this->args[i], args[i]);
	}

	// First, build the command scope (technically, shouldn't we create a fresh scope?)
	body->BuildRootTable(scope, false, true);

	string oldname = context.localscopename;
	context.localscopename = name;

	// Then evaluate the body of the command in the local scope
	body->Evaluate(scope, context);

	context.localscopename = oldname;

	delete scope;
	executing = false;
}

void ConstDef::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	// Const defs also do nothing on evaluation. They already defined their symbols in
	// the BuildRootTable pass, so now they just sit around looking pretty until somebody
	// else needs to evaluate the expressions they contain.
}

void ConstDef::EvaluateExpr(SymbolTable* scope, EvalContext& context, bool asbool)
{
	if(evaluating) {
		Error("recursion detected in evaluation of constant '" + this->name + "'");
		return;
	}
	evaluating = true;
	value->Evaluate(scope, context, asbool);
	evaluating = false;
}


void RomWrite::Evaluate(SymbolTable* scope, EvalContext& original_context, bool asbool)
{
	// Create a new context for the subexpressions
	EvalContext context;
	context.module = original_context.module;

	RomAccess* access = new RomAccess();

	// We keep track of our own internal labels here, instead of letting
	// the module manage them. The module simply adds its base address to
	// all label targets, while our labels ultimately should be measured
	// relative to the final write location of this RomWrite statement.
	access->internal_labels = new SymbolTable();
	context.labels = access->internal_labels;


	// Next we evaluate all applicable subexpressions, caching the results
	access->cache_base = new ByteChunk();
	context.output = access->cache_base;
	base->Evaluate(scope, context);
	if(size) {
		access->cache_size = new ByteChunk();
		context.output = access->cache_size;
		size->Evaluate(scope, context);
	}
	if(index) {
		access->cache_index = new ByteChunk();
		context.output = access->cache_index;
		index->Evaluate(scope, context);
	}
	access->cache_value = new ByteChunk();
	context.output = access->cache_value;
	value->Evaluate(scope, context);


	// TODO: registering a delayed write is really an operation of the compiler class,
	// not of any one module being compiled. Perhaps a reference to the compiler should
	// also be part of the context?
	context.module->RegisterRomWrite(access);
}

/*
 * Returns the virtual address 
 */
unsigned int RomAccess::GetVirtualAddress() const
{
	unsigned int base_adr = 0;
	unsigned int entry_size = 0;
	unsigned int entry_index = 0;

	base_adr = cache_base->ReadLong(0);

	if(cache_size)
		entry_size = cache_size->ReadLong(0);
	if(cache_index)
		entry_index = cache_index->ReadLong(0);

	return base_adr + entry_size * entry_index;
}

/*
 * Resolves any references contained in the code generated for this RomWrite
 */
void RomAccess::ResolveReferences()
{
	cache_base->ResolveReferences();
	if(cache_size) cache_size->ResolveReferences();
	if(cache_index) cache_index->ResolveReferences();

	// Before resolving refs in the value code, update internal label targets
	internal_labels->AddBaseAddress(GetVirtualAddress());
	cache_value->ResolveReferences();
}

void RomAccess::DoWrite(char* buffer, unsigned int address, int bufsize)
{
	cache_value->WriteChunk(buffer, address, bufsize);
}


void IdentExpr::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	/// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	//context.file = this->file;
	//context.line = this->linenumber;

	// To evaluate an identifier expression:
	// First, we have to look up the symbol and check the type of its value.
	// If it's a constant, we just evaluate the constant (provided there are no
	// parameters given; if there are parameters, we should report an error.)
	// If it's a command, we need to:
	//  - bind each argument expression to the corresponding symbol in the commands
	//     parameter list.
	//  - invoke the command.

	ByteChunk* output = context.output;
	Module* module = context.module;
	
	SymbolTable* lookupScope = scope;

	// If the ident expr's "file" field is not empty, we'll look it up in a different module
	if(!file.empty()) {
		Module* mod = module->GetSiblingContext(file);
		if(!mod) {
			Error("reference to nonexistent module '" + file + "'");
			return;
		}
		lookupScope = mod->GetRootTable();
	}

	Node* found = lookupScope->Lookup(name);

	if(found) {

		switch(found->GetType()) {
		
		// Constants get their own evaluation case
		case conststmt:
			if(hasparens) {
				Error("'" + GetFullName() + "' refers to a constant; cannot use parentheses");
				return;
			}
			((ConstDef*)found)->EvaluateExpr(scope, context, asbool);
			break;

		case commandstmt:
			{
			// First, set the scope of evaluation for each argument expression; this is
			// to ensure that all expressions are evaluated in their lexical scope (this
			// is necessary because parameters are evaluated lazily)
			for(unsigned int i = 0; i < args.size(); ++i)
				args[i]->scope = scope;

			CommandDef* cmd = (CommandDef*)found;

			if(cmd->GetArgCount() != args.size())
				Error("incorrect number of parameters to command '" + GetFullName() + "'");
			else
				cmd->Invoke(context, args);
			}
			break;

		default:
			if(found->IsExpression())
				((Expression*)found)->Evaluate(scope, context, asbool);
			else
				Error("invalid type");
		}

		return;
	}

	// Didn't find it in the symbol table, check the jumps table
	Label* foundlabel = lookupScope->LookupLabel(name);

	if(foundlabel) {
		if(hasparens) {
			Error("'" + GetFullName() + "' refers to a label; cannot use parentheses");
			return;
		}

		output->Long(foundlabel->GetTarget());

		// The targeted label might not have its address computed yet, so we register a
		// reference to the target label (unless refs are forbidden by the context)
		if(!context.norefs)
			output->AddReference(output->GetPos()-4, foundlabel);
		return;
	}

	Error("use of undefined identifier '" + GetFullName() + "'");
}

void Label::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	using std::cout;
	using std::endl;

	// Set this label's position to be the current offset within the code output

	// However! If this label is inside a command body, it might be evaluated more
	// than once, so we don't actually rely on this lexical label object. Instead,
	// we do this:
	//
	// 1. Look up this label name in the current scope
	//    (We'll get a label that was created previously in BuildRootTable())
	// 2. Set _that_ label's target location
	// 3. Register the label _globally_ under a new name.
	// 
	// The global name will be inaccessible to the rest of the program; its only
	// purpose is to allow the label's position to be updated when the module base
	// address is set. We have to do a similar thing with the internal labels created
	// by menu and if statements.
	
	Label* theLabel = scope->LookupLabel(GetName());

	if(!theLabel) {
		Error("label evaluation lookup failed for '" + GetName() + "' - probable internal compiler error!");
		return;
	}
	
	theLabel->SetTarget( context.output->GetPos() );

	string globalname;
	// If there's a local scope name, be sure to assign a unique identifier.
	// Otherwise, it's alright to just use the label name.
	if(!context.localscopename.empty())
		globalname = context.localscopename + "." + name + "." + context.GetUniqueLabelName();
	else
		globalname = name;

	context.DefineLabel(globalname, theLabel);
}

void ExprStmt::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	expr->Evaluate(scope, context);
}

void AndExpr::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	// Lowering A and B:
	//  [A]
	//  [iffalse goto end]
	//  [B]
	//  label end:

	ByteChunk* output = context.output;

	// Create internal label
	string labelbase = context.GetUniqueLabelName();
	Label* endlabel = new Label(labelbase + ".end");

	context.DefineLabel(endlabel);

	// Evaluate the first operand
	a->Evaluate(scope, context, true);

	// Add a jump to the end if the first operand is false
	output->Code("1B 02 FF FF FF FF");
	output->AddReference(output->GetPos()-4, endlabel);

	// TODO:
	//  Hm. I just realized that some boolean expressions (and and or) rely on reference
	//  resolution to operate correctly. Thus, it doesn't make sense to use them in ROM
	//  write statements at the moment, because ROM write statements occur after normal
	//  resolution, but without doing any resolution themselves. Perhaps ROM write statements
	//  should have a special resolution step to take care of stuff like this. (Perhaps
	//  the ROM data itself should be represented as a ByteChunk, with refs?)
	//  Anyway, don't worry about this for now, since using boolean expressions in a ROM
	//  write statement is not a very likely usage scenario.
	// UPDATE: 11/11/2008
	//  This issue has been fixed by moving the evaluation of ROM write subexpressions
	//  back to the evaluation pass of the compiler, and simply caching the results and
	//  resolving references in a later pass. However, it still might be worthwhile to
	//  consider alternative solutions; whole-ROM resolution seems interesting for example.

	// Evaluate the second operand
	b->Evaluate(scope, context, true);

	// Set the position of the end label
	endlabel->SetTarget(output->GetPos());
}

void OrExpr::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	// Lowering of A or B:
	//  [A]
	//  [iftrue goto end]
	//  [B]
	//  label end:
	ByteChunk* output = context.output;

	string labelbase = context.GetUniqueLabelName();
	Label* endlabel = new Label(labelbase + ".end");
	context.DefineLabel(endlabel);

	// a
	a->Evaluate(scope, context, true);
	// iftrue goto end:
	output->Code("1B 03 FF FF FF FF");
	output->AddReference(output->GetPos()-4, endlabel);
	// b
	b->Evaluate(scope, context, true);
	// end:
	endlabel->SetTarget(output->GetPos());
}

void NotExpr::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	// Lowering of not A:
	// [A]			; assumes A modifies the W register
	// [0B 00]		; set W = (W == 0)
	a->Evaluate(scope, context, true);
	context.output->Code("0B 00");
}

void FlagExpr::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	// When evaluating as a boolean, we want to use the "load flag" command, 07.
	// This is so an expression "flag <x>" can be used in normal expressions as
	// a flag number (e.g., set(myflag)) and in boolean conditions (e.g.,
	//  if myflag { }). Just sugar, really.
	// Thus, if statements and "and", "or", "not" expressions pass asbool=true.
	// All other nodes ignore this parameter, using the default value of false.
	//
	// Honestly, this feels like a hack, and it comes at the cost of some possibly
	// unintuitive behavior. However, it seems useful to allow the use of flags
	// in conditionals this way... Maybe there's a better alternative.

	if(asbool) context.output->Code("07");
	context.output->Byte(flag & 255);
	context.output->Byte((flag >> 8) & 255);
}

void IntLiteral::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// An integer expression is lowered as four bytes, little endian
	ByteChunk* output = context.output;
	output->Byte(value & 255);
	output->Byte((value >> 8) & 255);
	output->Byte((value >> 16) & 255);
	output->Byte((value >> 24) & 255);
}

void StringLiteral::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	// Use a stringparser to evaluate self
	StringParser parser(value, linenumber, e);
	parser.Parse(scope, context);
}


void BoundedExpr::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	/// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;
	// TODO: there really has to be a better way to handle these scope overrides.
	//  Having to put this if statement at the top of certain evaluate methods is kludgy.
	//  Probably the logic for deciding the evaluation scope of a node should be at
	//  a higher level, and all nodes should have their scope of evaluation set that way.

	ByteChunk* output = context.output;

	// Create a new bytechunk in which to evaluate the expression
	ByteChunk raw;
	context.output = &raw;
	expr->Evaluate(scope, context);

	int pos;
	if(index < 0)
		pos = 0;
	else
		pos = size * index;

	int out_pos = output->GetPos();
	for(int i = 0; i < size; ++i)
		output->Byte(raw.ReadByte(pos + i));

	// Oops! What if the expression we just evaluated contained references to labels?
	// Evaluating it would have registered references with the "raw" bytechunk, not the
	// one we are using for final output.
	
	// Copy references from the raw chunk to the output chunk
	vector<ByteChunk::Reference> refs = raw.GetReferencesInRange(pos,size);
	for(unsigned int i = 0; i < refs.size(); ++i) {
		ByteChunk::Reference r = refs[i];

		// the "local" position, when translated into this expression's range
		int loc = r.location - pos;

		// Deal with truncated references
		if(loc < 0 || loc + 3 >= size)
		{
			// how many bytes were cut off at the beginning?
			int ref_offset = -(std::min(loc, 0));
			// how many bytes were cut off the end?
			int over = std::min(4, (loc+4)-size);
			// how many bytes of the reference are left?
			int ref_length = std::min(4, 4 - ref_offset - over);

			output->AddReference(loc + out_pos, ref_offset, ref_length, r.target);
		}
		else
			output->AddReference(loc + out_pos, r.target);
	}


	// IMPORTANT: reset the original context's output
	context.output = output;
}


void Program::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	for(unsigned int i = 0; i < stmts.size(); ++i)
		stmts[i]->Evaluate(scope, context);
}



/*
 * AST "pretty-printing" methods.
 * Pretty much only used during debugging, when a nicely-formatted
 * representation of the parsed program is desired.
 */
string Block::ToString(const string& indent, bool suppress) const {
	string result = "{\n";
	for(unsigned int i = 0; i < stmts.size(); ++i)
		result += stmts[i]->ToString(nest(indent)) + "\n";
	result += indent + "}";
	return result;
}

string IfStatement::ToString(const string& indent, bool suppress) const {
	string result = indent + "if ";
	result += condition->ToString(indent) + " ";
	result += thenstmt->ToString(indent, true) + " ";
	if(elsestmt != NULL) {
		result += "else ";
		result += elsestmt->ToString(indent, true);
	}
	return result;
}

string MenuStatement::ToString(const string& indent, bool suppress) const {
	string result = indent + "menu ";
	if(columns != exprs.size()) {
		stringstream ss;
		ss << columns;
		result += ss.str() + " ";
	}
	result += "{\n";
	for(unsigned int i = 0; i < exprs.size(); ++i) {
		result += nest(indent) + exprs[i]->ToString(indent) + ": " + stmts[i]->ToString(nest(indent),true) + "\n";
	}
	result += indent + "}";
	return result;
}

string CommandDef::ToString(const string& indent, bool suppress) const {
	string result = indent + "command " + name + " ";
	if(args.size() > 0) {
		result += "(" + args[0];
		for(unsigned int i = 1; i < args.size(); ++i)
			result += "," + args[i];
		result += ") ";
	}
	return result + body->ToString(indent);
}

string ConstDef::ToString(const string& indent, bool suppress) const {
	return indent + "define " + name + " = " + value->ToString(indent);
}

string RomWrite::ToString(const string& indent, bool suppress) const {
	string result = indent;
	if(size) {
		result += "ROMTBL[";
		result += base->ToString("",true) + ", ";
		result += size->ToString("",true) + ", ";
		result += index->ToString("",true) + "] = ";
	}
	else {
		result += "ROM[";
		result += base->ToString("",true) + "]";
	}
	result += value->ToString("",true);
	return result;
}

string Label::ToString(const string& indent, bool suppress) const {
	return indent + name + ": ";
}

string ExprStmt::ToString(const string& indent, bool suppress) const {
	if(suppress)
		return expr->ToString(indent);
	return indent + expr->ToString(indent);
}

string FlagExpr::ToString(const string&indent, bool suppress) const {
	stringstream s;
	s << flag;
	return "flag " + s.str();
}

string IntLiteral::ToString(const string& indent, bool suppress) const {
	stringstream s;
	s << value;
	return s.str();
}

string StringLiteral::ToString(const string& indent, bool suppress) const {
	return "\"" + value + "\"";
}

string AndExpr::ToString(const string& indent, bool s) const {
	return "(" + a->ToString(indent) + " and " + b->ToString(indent) + ")";
}

string OrExpr::ToString(const string& indent, bool s) const {
	return "(" + a->ToString(indent) + " or " + b->ToString(indent) + ")";
}

string NotExpr::ToString(const string& indent, bool s) const {
	return "not " + a->ToString(indent);
}

string IdentExpr::ToString(const string& indent, bool s) const {
	string result;
	if(file != "")
		result += file + ".";
	result += name;
	if(args.size() > 0) {
		result += "(" + args[0]->ToString(indent,s);
		for(unsigned int i = 1; i < args.size(); ++i)
			result += ", " + args[i]->ToString(indent,s);
		result += ")";
	}
	else if(hasparens)
		result += "()";
	return  result;
}


string BoundedExpr::ToString(const string &indent, bool s) const
{
	stringstream ss;
	switch(size) {
		case 1: ss << "byte "; break;
		case 2: ss << "short "; break;
		case 4: ss << "long "; break;
		default:
			ss << "maxbytes " << size << " ";
	}
	if(index != -1) {
		ss << "[" << index << "] ";
	}
	ss << expr->ToString(indent, s);
	return ss.str();
}


string Program::ToString(const string& indent, bool s) const {
	string result = "";
	for(unsigned int i = 0; i < stmts.size(); ++i)
		result += stmts[i]->ToString(indent) + "\n";
	return result;
}

